﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;


namespace advadev.Windows.DragDrop
{

    /// <summary>
    /// Represents a drag drop group
    /// </summary>
    internal class DragDropGroup : Grid
    {

        /// <summary>for internal use</summary>
        private bool _isTouchActive;
        /// <summary>for internal use</summary>
        private DragDropContainer _parentDragDropContainer;
        /// <summary>for internal use</summary>
        private UIElement _draggedElement;
        /// <summary>for internal use</summary>
        private Border _dragThumb;
        /// <summary>for internal use</summary>
        private Point _relativeDragPoint;
        /// <summary>for internal use</summary>
        private Point _startOffset;
        /// <summary>for internal use</summary>
        private Transform _originalTransform;
        /// <summary>for internal use</summary>
        private EventHandler _onIsDraggableChangedEventHandler;
        /// <summary>for internal use</summary>
        private List<UIElement> _groupElements = new List<UIElement>();


        /// <summary>
        /// Initializes a new instance of the DragDropGroup class
        /// </summary>
        internal DragDropGroup(DragDropContainer parentDragDropContainer)
        {
            _parentDragDropContainer = parentDragDropContainer;
            _dragThumb = new Border();
            _dragThumb.HorizontalAlignment = HorizontalAlignment.Left;
            _dragThumb.VerticalAlignment = VerticalAlignment.Top;
            _onIsDraggableChangedEventHandler = new EventHandler((object sender, EventArgs args) =>
            {
                UIElement groupElement = sender as UIElement;
                if (groupElement != null)
                {
                    bool isDraggable = DragDropContainer.GetIsDraggable(groupElement);
                    if (isDraggable)
                    {
                        registerDragSourceHandler(groupElement);
                    }
                    else
                    {
                        unregisterDragSourceHandler(groupElement);
                    }
                }
            });
        }


        #region dependency properties
        static internal readonly DependencyProperty DragDropGroupNameProperty = DependencyProperty.Register("DragDropGroupName", typeof(string), typeof(DragDropGroup), new UIPropertyMetadata(string.Empty));
        internal string DragDropGroupName
        {
            get { return (string)GetValue(DragDropGroupNameProperty); }
            set { SetValue(DragDropGroupNameProperty, value); }
        }
        #endregion dependency properties


        /// <summary>
        /// Gets a flag indicating whether the drag drop group has group
        /// elements
        /// </summary>
        internal bool HasGroupElements
        {
            get
            {
                return _groupElements.Count > 0;
            }
        }


        /// <summary>
        /// Adds the given group element
        /// </summary>
        /// <param name="groupElement">
        /// Group element
        /// </param>
        internal void AddGroupElement(UIElement groupElement)
        {
            if (groupElement != null)
            {
                if (!_groupElements.Contains(groupElement))
                {
                    _groupElements.Add(groupElement);
                    groupElement.IsVisibleChanged += new DependencyPropertyChangedEventHandler(groupElement_IsVisibleChanged);
                    if (groupElement.IsVisible)
                    {
                        registerIsDraggableChangedHandler(groupElement);
                        bool isDraggable = DragDropContainer.GetIsDraggable(groupElement);
                        if (isDraggable)
                        {
                            registerDragSourceHandler(groupElement);
                        }
                        if (_draggedElement != null)
                        {
                            DragDropContainer.SetIsDragActive(groupElement, true);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Removes the given group element
        /// </summary>
        /// <param name="groupElement">
        /// Group element
        /// </param>
        internal void RemoveGroupElement(UIElement groupElement)
        {
            if (groupElement != null)
            {
                if (_groupElements.Contains(groupElement))
                {
                    if (IsVisible)
                    {
                        if (_draggedElement != null)
                        {
                            DragDropContainer.SetIsDragActive(groupElement, false);
                        }
                        bool isDraggable = DragDropContainer.GetIsDraggable(groupElement);
                        if (isDraggable)
                        {
                            unregisterDragSourceHandler(groupElement);
                        }
                        unregisterIsDraggableChangedHandler(groupElement);
                    }
                    groupElement.IsVisibleChanged -= new DependencyPropertyChangedEventHandler(groupElement_IsVisibleChanged);
                    _groupElements.Remove(groupElement);
                }
            }
        }

        /// <summary>
        /// Handles the "IsVisibleChanged" event of a group element
        /// </summary>
        /// <param name="sender">
        /// Sender of the event
        /// </param>
        /// <param name="args">
        /// Event argument
        /// </param>
        private void groupElement_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs args)
        {
            UIElement groupElement = sender as UIElement;
            if (groupElement != null)
            {
                if (groupElement.IsVisible)
                {
                    registerIsDraggableChangedHandler(groupElement);
                    bool isDraggable = DragDropContainer.GetIsDraggable(groupElement);
                    if (isDraggable)
                    {
                        registerDragSourceHandler(groupElement);
                    }
                    if (_draggedElement != null)
                    {
                        DragDropContainer.SetIsDragActive(groupElement, true);
                    }
                }
                else
                {
                    if (_draggedElement != null)
                    {
                        DragDropContainer.SetIsDragActive(groupElement, false);
                    }
                    bool isDraggable = DragDropContainer.GetIsDraggable(groupElement);
                    if (isDraggable)
                    {
                        unregisterDragSourceHandler(groupElement);
                    }
                    unregisterIsDraggableChangedHandler(groupElement);
                }
            }
        }

        /// <summary>
        /// Registers a is draggable changed handler for the given group element
        /// </summary>
        /// <param name="groupElement">
        /// Group element
        /// </param>
        private void registerIsDraggableChangedHandler(UIElement groupElement)
        {
            DependencyPropertyDescriptor isDraggableDescriptor = DependencyPropertyDescriptor.FromProperty(DragDropContainer.IsDraggableProperty, typeof(DragDropGroup));
            if (isDraggableDescriptor != null)
            {
                isDraggableDescriptor.AddValueChanged(groupElement, _onIsDraggableChangedEventHandler);
            }
        }

        /// <summary>
        /// Unregisters a is draggable changed handler for the given group
        /// element
        /// </summary>
        /// <param name="groupElement">
        /// Group element
        /// </param>
        private void unregisterIsDraggableChangedHandler(UIElement groupElement)
        {
            DependencyPropertyDescriptor isDraggableDescriptor = DependencyPropertyDescriptor.FromProperty(DragDropContainer.IsDraggableProperty, typeof(DragDropGroup));
            if (isDraggableDescriptor != null)
            {
                isDraggableDescriptor.RemoveValueChanged(groupElement, _onIsDraggableChangedEventHandler);
            }
        }

        /// <summary>
        /// Registers the drag source handler for the given group element
        /// </summary>
        /// <param name="groupElement">
        /// Group element
        /// </param>
        private void registerDragSourceHandler(UIElement groupElement)
        {
            groupElement.MouseLeftButtonDown += new MouseButtonEventHandler(groupElement_MouseLeftButtonDown);
            groupElement.IsManipulationEnabled = true;
            groupElement.ManipulationStarting += new EventHandler<ManipulationStartingEventArgs>(groupElement_ManipulationStarting);
            groupElement.ManipulationDelta += new EventHandler<ManipulationDeltaEventArgs>(groupElement_ManipulationDelta);
            groupElement.ManipulationCompleted += new EventHandler<ManipulationCompletedEventArgs>(groupElement_ManipulationCompleted);
        }

        /// <summary>
        /// Unregisters the drag source handler for the given group element
        /// </summary>
        /// <param name="groupElement">
        /// Group element
        /// </param>
        private void unregisterDragSourceHandler(UIElement groupElement)
        {
            groupElement.ManipulationCompleted += new EventHandler<ManipulationCompletedEventArgs>(groupElement_ManipulationCompleted);
            groupElement.ManipulationDelta += new EventHandler<ManipulationDeltaEventArgs>(groupElement_ManipulationDelta);
            groupElement.ManipulationStarting += new EventHandler<ManipulationStartingEventArgs>(groupElement_ManipulationStarting);
            groupElement.IsManipulationEnabled = false;
            groupElement.MouseLeftButtonDown -= new MouseButtonEventHandler(groupElement_MouseLeftButtonDown);
        }

        /// <summary>
        /// Handles the "MouseLeftButtonDown" event of a group element
        /// </summary>
        /// <param name="sender">
        /// Sender of the event
        /// </param>
        /// <param name="args">
        /// Event argument
        /// </param>
        private void groupElement_MouseLeftButtonDown(object sender, MouseButtonEventArgs args)
        {
            _draggedElement = sender as UIElement;
            if (_draggedElement != null)
            {
                _isTouchActive = false;
                args.Handled = true;
                startDragDrop();
            }
        }

        /// <summary>
        /// Handles the "ManipulationStarting" event of a group element
        /// </summary>
        /// <param name="sender">
        /// Sender of the event
        /// </param>
        /// <param name="args">
        /// Event argument
        /// </param>
        private void groupElement_ManipulationStarting(object sender, ManipulationStartingEventArgs args)
        {
            if (args != null)
            {
                _draggedElement = sender as UIElement;
                if (_draggedElement != null)
                {
                    _isTouchActive = true;
                    args.ManipulationContainer = this;
                    args.Handled = true;
                    startDragDrop();
                }
            }
        }

        /// <summary>
        /// Starts the drag drop
        /// </summary>
        private void startDragDrop()
        {
            _originalTransform = _draggedElement.RenderTransform;
            DragDropContainer.SetIsDragged(_draggedElement, true);
            foreach (UIElement groupElement in _groupElements)
            {
                DropCommand sourceDropCommand = DragDropContainer.GetSourceDropCommand(_draggedElement);
                object sourceDropCommandParameter = DragDropContainer.GetSourceDropCommandParameter(_draggedElement);
                DropCommand targetDropCommand = DragDropContainer.GetTargetDropCommand(groupElement);
                object targetDropCommandParameter = DragDropContainer.GetTargetDropCommandParameter(groupElement);
                if (sourceDropCommand != null)
                {
                    if (!sourceDropCommand.CanExecute(this, new DropCommandParameter(_draggedElement, groupElement, sourceDropCommandParameter, targetDropCommandParameter)))
                    {
                        continue;
                    }
                }
                if (targetDropCommand != null)
                {
                    if (!targetDropCommand.CanExecute(this, new DropCommandParameter(_draggedElement, groupElement, sourceDropCommandParameter, targetDropCommandParameter)))
                    {
                        continue;
                    }
                }
                bool isDropTarget = DragDropContainer.GetIsDropTarget(groupElement);
                if (isDropTarget)
                {
                    DragDropContainer.SetIsDragActive(groupElement, true);
                }
            }
            _draggedElement.RenderTransform = new TransformGroup();
            (_draggedElement.RenderTransform as TransformGroup).Children.Add(new TranslateTransform(0d, 0d));
            (_draggedElement.RenderTransform as TransformGroup).Children.Add(new TranslateTransform(0d, 0d));
            _parentDragDropContainer.MouseEnter += new MouseEventHandler(DragDropGroup_MouseEnter);
            _parentDragDropContainer.MouseMove += new MouseEventHandler(DragDropGroup_MouseMove);
            _parentDragDropContainer.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(DragDropGroup_MouseLeftButtonUp);
            initializeDragThumb();
            updateDragThumbPosition();
            InternalChildren.Add(_dragThumb);
        }

        /// <summary>
        /// Handles the "MouseEnter" event of the drag drop group
        /// </summary>
        /// <param name="sender">
        /// Sender of the event
        /// </param>
        /// <param name="args">
        /// Event argument
        /// </param>
        private void DragDropGroup_MouseEnter(object sender, MouseEventArgs args)
        {
            if (args != null && args.LeftButton == MouseButtonState.Released)
            {
                InternalChildren.Remove(_dragThumb);
                _parentDragDropContainer.PreviewMouseLeftButtonUp -= new MouseButtonEventHandler(DragDropGroup_MouseLeftButtonUp);
                _parentDragDropContainer.MouseMove -= new MouseEventHandler(DragDropGroup_MouseMove);
                _parentDragDropContainer.MouseEnter -= new MouseEventHandler(DragDropGroup_MouseEnter);
                _draggedElement.RenderTransform = _originalTransform;
                foreach (UIElement groupElement in _groupElements)
                {
                    DragDropContainer.SetIsDragActive(groupElement, false);
                    DragDropContainer.SetIsActiveDropTarget(groupElement, false);
                }
                DragDropContainer.SetIsDragged(_draggedElement, false);
                _draggedElement = null;
            }
        }

        /// <summary>
        /// Handles the "MouseMove" event of the drag drop group
        /// </summary>
        /// <param name="sender">
        /// Sender of the event
        /// </param>
        /// <param name="args">
        /// Event argument
        /// </param>
        private void DragDropGroup_MouseMove(object sender, MouseEventArgs args)
        {
            if (!_isTouchActive)
            {
                updateDragThumbPosition();
                updateDragDropTargets();
            }
        }

        /// <summary>
        /// Handles the "ManipulationDelta" event of a group element
        /// </summary>
        /// <param name="sender">
        /// Sender of the event
        /// </param>
        /// <param name="args">
        /// Event argument
        /// </param>
        private void groupElement_ManipulationDelta(object sender, ManipulationDeltaEventArgs args)
        {
            if (args != null)
            {
                if (args.DeltaManipulation != null)
                {
                    if (args.DeltaManipulation.Translation != null)
                    {
                        FrameworkElement draggedFrameworkElement = _draggedElement as FrameworkElement;
                        if (draggedFrameworkElement != null)
                        {
                            Point currentPosition = new Point(((_draggedElement.RenderTransform as TransformGroup).Children[0] as TranslateTransform).X + args.DeltaManipulation.Translation.X, ((_draggedElement.RenderTransform as TransformGroup).Children[0] as TranslateTransform).Y + args.DeltaManipulation.Translation.Y);
                            ((_draggedElement.RenderTransform as TransformGroup).Children[0] as TranslateTransform).X = 0;
                            ((_draggedElement.RenderTransform as TransformGroup).Children[0] as TranslateTransform).Y = 0;
                            ((_draggedElement.RenderTransform as TransformGroup).Children[1] as TranslateTransform).X = 0;
                            ((_draggedElement.RenderTransform as TransformGroup).Children[1] as TranslateTransform).Y = 0;
                            Point currentOffset = draggedFrameworkElement.TransformToAncestor(_parentDragDropContainer).Transform(new Point(0d, 0d));
                            ((_draggedElement.RenderTransform as TransformGroup).Children[0] as TranslateTransform).X = currentPosition.X;
                            ((_draggedElement.RenderTransform as TransformGroup).Children[0] as TranslateTransform).Y = currentPosition.Y;
                            ((_draggedElement.RenderTransform as TransformGroup).Children[1] as TranslateTransform).X = _startOffset.X - currentOffset.X;
                            ((_draggedElement.RenderTransform as TransformGroup).Children[1] as TranslateTransform).Y = _startOffset.Y - currentOffset.Y;
                            draggedFrameworkElement.InvalidateVisual();
                        }
                    }
                }
                updateDragDropTargets();
                args.Handled = true;
            }
        }

        /// <summary>
        /// Updates the drag drop targets
        /// </summary>
        private void updateDragDropTargets()
        {
            UIElement dropTarget = findDropTargetAtCurrentPosition();
            if (dropTarget != null)
            {
                DragDropContainer.SetIsActiveDropTarget(dropTarget, true);
            }
            foreach (UIElement groupElement in _groupElements)
            {
                if (groupElement != dropTarget)
                {
                    DragDropContainer.SetIsActiveDropTarget(groupElement, false);
                }
            }
        }

        /// <summary>
        /// Handles the "MouseLeftButtonUp" event of the drag drop group
        /// </summary>
        /// <param name="sender">
        /// Sender of the event
        /// </param>
        /// <param name="args">
        /// Event argument
        /// </param>
        private void DragDropGroup_MouseLeftButtonUp(object sender, MouseButtonEventArgs args)
        {
            endDragDrop();
        }

        /// <summary>
        /// Handles the "ManipulationCompleted" event of a group element
        /// </summary>
        /// <param name="sender">
        /// Sender of the event
        /// </param>
        /// <param name="args">
        /// Event argument
        /// </param>
        private void groupElement_ManipulationCompleted(object sender, ManipulationCompletedEventArgs args)
        {
            endDragDrop();
            _isTouchActive = false;
        }

        /// <summary>
        /// Ends the drag drop
        /// </summary>
        private void endDragDrop()
        {
            UIElement targetElement = findDropTargetAtCurrentPosition();
            InternalChildren.Remove(_dragThumb);
            if (targetElement != null)
            {
                Point offset = new Point(((_draggedElement.RenderTransform as TransformGroup).Children[0] as TranslateTransform).X, ((_draggedElement.RenderTransform as TransformGroup).Children[0] as TranslateTransform).Y);
                DropCommand sourceDropCommand = DragDropContainer.GetSourceDropCommand(_draggedElement);
                object sourceDropCommandParameter = DragDropContainer.GetSourceDropCommandParameter(_draggedElement);
                DropCommand targetDropCommand = DragDropContainer.GetTargetDropCommand(targetElement);
                object targetDropCommandParameter = DragDropContainer.GetTargetDropCommandParameter(targetElement);
                if (sourceDropCommand != null)
                {
                    sourceDropCommand.Execute(this, new DropCommandParameter(_draggedElement, targetElement, sourceDropCommandParameter, targetDropCommandParameter, offset));
                }
                if (targetDropCommand != null)
                {
                    targetDropCommand.Execute(this, new DropCommandParameter(_draggedElement, targetElement, sourceDropCommandParameter, targetDropCommandParameter, offset));
                }
            }
            else
            {
                Point offset = new Point(((_draggedElement.RenderTransform as TransformGroup).Children[0] as TranslateTransform).X, ((_draggedElement.RenderTransform as TransformGroup).Children[0] as TranslateTransform).Y);
                DropCommand sourceDropCommand = DragDropContainer.GetSourceDropCommand(_draggedElement);
                object sourceDropCommandParameter = DragDropContainer.GetSourceDropCommandParameter(_draggedElement);
                if (sourceDropCommand != null)
                {
                    sourceDropCommand.Execute(this, new DropCommandParameter(_draggedElement, null, sourceDropCommandParameter, null, offset));
                }
            }
            _parentDragDropContainer.PreviewMouseLeftButtonUp -= new MouseButtonEventHandler(DragDropGroup_MouseLeftButtonUp);
            _parentDragDropContainer.MouseMove -= new MouseEventHandler(DragDropGroup_MouseMove);
            _parentDragDropContainer.MouseEnter -= new MouseEventHandler(DragDropGroup_MouseEnter);
            _draggedElement.RenderTransform = _originalTransform;
            foreach (UIElement groupElement in _groupElements)
            {
                DragDropContainer.SetIsDragActive(groupElement, false);
                DragDropContainer.SetIsActiveDropTarget(groupElement, false);
            }
            DragDropContainer.SetIsDragged(_draggedElement, false);
            _draggedElement = null;
        }

        /// <summary>
        /// Initializes the drag thumb
        /// </summary>
        private void initializeDragThumb()
        {
            FrameworkElement draggedFrameworkElement = _draggedElement as FrameworkElement;
            if (draggedFrameworkElement != null)
            {
                Binding heightBinding = new Binding();
                heightBinding.Source = _draggedElement;
                heightBinding.Path = new PropertyPath("ActualHeight");
                _dragThumb.SetBinding(Border.HeightProperty, heightBinding);
                Binding widthBinding = new Binding();
                widthBinding.Source = _draggedElement;
                widthBinding.Path = new PropertyPath("ActualWidth");
                _dragThumb.SetBinding(Border.WidthProperty, widthBinding);
                _dragThumb.SnapsToDevicePixels = true;
                //_dragThumb.Height = draggedFrameworkElement.ActualHeight + 1000;
                //_dragThumb.Width = draggedFrameworkElement.ActualWidth + 1000;
                //_dragThumb.Margin = new Thickness(-500, -500, -500, -500);
                _dragThumb.Background = new VisualBrush(_draggedElement) { AlignmentX = AlignmentX.Center, AlignmentY = AlignmentY.Center, Stretch = Stretch.None };
                _dragThumb.Cursor = draggedFrameworkElement.Cursor;
                _startOffset = draggedFrameworkElement.TransformToAncestor(_parentDragDropContainer).Transform(new Point(0d, 0d));
                _dragThumb.RenderTransform = new TransformGroup();
                (_dragThumb.RenderTransform as TransformGroup).Children.Add(new TranslateTransform(_startOffset.X, _startOffset.Y));
                (_dragThumb.RenderTransform as TransformGroup).Children.Add((_draggedElement.RenderTransform as TransformGroup).Children[0]);
                _relativeDragPoint = Mouse.GetPosition(_parentDragDropContainer);
            }
        }

        /// <summary>
        /// Updates the drag thumb position
        /// </summary>
        private void updateDragThumbPosition()
        {
            FrameworkElement draggedFrameworkElement = _draggedElement as FrameworkElement;
            if (draggedFrameworkElement != null)
            {
                Point currentPosition = Mouse.GetPosition(_parentDragDropContainer);
                ((_draggedElement.RenderTransform as TransformGroup).Children[0] as TranslateTransform).X = 0;
                ((_draggedElement.RenderTransform as TransformGroup).Children[0] as TranslateTransform).Y = 0;
                ((_draggedElement.RenderTransform as TransformGroup).Children[1] as TranslateTransform).X = 0;
                ((_draggedElement.RenderTransform as TransformGroup).Children[1] as TranslateTransform).Y = 0;
                Point currentOffset = draggedFrameworkElement.TransformToAncestor(_parentDragDropContainer).Transform(new Point(0d, 0d));
                ((_draggedElement.RenderTransform as TransformGroup).Children[0] as TranslateTransform).X = currentPosition.X - _relativeDragPoint.X;
                ((_draggedElement.RenderTransform as TransformGroup).Children[0] as TranslateTransform).Y = currentPosition.Y - _relativeDragPoint.Y;
                ((_draggedElement.RenderTransform as TransformGroup).Children[1] as TranslateTransform).X = _startOffset.X - currentOffset.X;
                ((_draggedElement.RenderTransform as TransformGroup).Children[1] as TranslateTransform).Y = _startOffset.Y - currentOffset.Y;
                draggedFrameworkElement.InvalidateVisual();
            }
        }

        /// <summary>
        /// Finds the (nearest) drop target at the current position
        /// </summary>
        /// <returns>
        /// Drop target or null
        /// </returns>
        private UIElement findDropTargetAtCurrentPosition()
        {
            Point topLeftDraggedElementCorner = _dragThumb.TransformToAncestor(_parentDragDropContainer).Transform(new Point(0, 0));
            Point bottomRightDraggedElementCorner = new Point(topLeftDraggedElementCorner.X + _dragThumb.ActualWidth, topLeftDraggedElementCorner.Y + _dragThumb.ActualHeight);
            int draggedElementStartX = (int)Math.Round(Math.Min(topLeftDraggedElementCorner.X, bottomRightDraggedElementCorner.X));
            int draggedElementStartY = (int)Math.Round(Math.Min(topLeftDraggedElementCorner.Y, bottomRightDraggedElementCorner.Y));
            int draggedElementWidth = (int)Math.Round(Math.Abs(bottomRightDraggedElementCorner.X - topLeftDraggedElementCorner.X));
            int draggedElementHeight = (int)Math.Round(Math.Abs(bottomRightDraggedElementCorner.Y - topLeftDraggedElementCorner.Y));
            Point draggedElementCenter = new Point(topLeftDraggedElementCorner.X + (_dragThumb.ActualWidth / 2d), topLeftDraggedElementCorner.Y + (_dragThumb.ActualHeight / 2d));
            List<KeyValuePair<double, FrameworkElement>> targets = new List<KeyValuePair<double, FrameworkElement>>();
            foreach (UIElement groupElement in _groupElements)
            {
                if (groupElement == _draggedElement)
                {
                    continue;
                }
                if (groupElement.IsVisible)
                {
                    if (_draggedElement.IsAncestorOf(groupElement))
                    {
                        continue;
                    }
                    bool isDragActive = DragDropContainer.GetIsDragActive(groupElement);
                    if (!isDragActive)
                    {
                        continue;
                    }
                    FrameworkElement groupFrameworkElement = groupElement as FrameworkElement;
                    if (groupFrameworkElement != null)
                    {
                        Point topLeftTargetElementCorner = groupFrameworkElement.TransformToAncestor(_parentDragDropContainer).Transform(new Point(0d, 0d));
                        Point bottomRightTargetElementCorner = new Point(topLeftTargetElementCorner.X + groupFrameworkElement.ActualWidth, topLeftTargetElementCorner.Y + groupFrameworkElement.ActualHeight);
                        int targetElementStartX = (int)Math.Round(Math.Min(topLeftTargetElementCorner.X, bottomRightTargetElementCorner.X));
                        int targetElementStartY = (int)Math.Round(Math.Min(topLeftTargetElementCorner.Y, bottomRightTargetElementCorner.Y));
                        int targetElementWidth = (int)Math.Round(Math.Abs(bottomRightTargetElementCorner.X - topLeftTargetElementCorner.X));
                        int targetElementHeight = (int)Math.Round(Math.Abs(bottomRightTargetElementCorner.Y - topLeftTargetElementCorner.Y));
                        if (!System.Drawing.Rectangle.Intersect(new System.Drawing.Rectangle(draggedElementStartX, draggedElementStartY, draggedElementWidth, draggedElementHeight), new System.Drawing.Rectangle(targetElementStartX, targetElementStartY, targetElementWidth, targetElementHeight)).IsEmpty)
                        {
                            double offsetX = Math.Abs(draggedElementCenter.X - (topLeftTargetElementCorner.X + (groupFrameworkElement.ActualWidth / 2d)));
                            double offsetY = Math.Abs(draggedElementCenter.Y - (topLeftTargetElementCorner.Y + (groupFrameworkElement.ActualHeight / 2d)));
                            targets.Add(new KeyValuePair<double, FrameworkElement>((Math.Sqrt((offsetX * offsetX) + (offsetY * offsetY))), groupFrameworkElement));
                        }
                    }
                }
            }
            if (targets.Count > 0)
            {
                double nearestDistance = double.MaxValue;
                FrameworkElement nearestDropTarget = null;
                foreach (KeyValuePair<double, FrameworkElement> pair in targets)
                {
                    if (pair.Key < nearestDistance)
                    {
                        nearestDistance = pair.Key;
                        nearestDropTarget = pair.Value;
                    }
                }
                return nearestDropTarget;
            }
            return null;
        }

    }

}
